local function ptab(tab, ind, seen)
    if not seen then
        seen = {}
    end
    if seen[tab] then return end
    seen[tab] = true
    local istr = ''
    if ind and (ind > 0) then
        for i = 0, ind do
            istr = istr..' '
        end
    end
    print(istr..'{')
    for k, v in pairs(tab) do
        if type(v) == 'table' then
            print(istr..' '..k..'=')
            if seen[v] then
                print(istr..' '..'<recursive ref>')
            else
                ptab(v, ind or 0 + 1, seen)
            end
        else
            print(istr..' '..k..'='..tostring(v))
        end
    end
    print(istr..'}')
end

local counts = {
    numbers = 0,
    strings = 0,
    tables = 0,
    functions = 0,
    func_size = 0,
    str_size = 0,
}

local function update_counts_for(v)
    if type(v) == 'string' then
        counts.strings = counts.strings + 1
        counts.str_size = counts.str_size + #v
    elseif type(v) == 'function' then
        counts.functions = counts.functions + 1
        --counts.func_size = counts.func_size + #string.dump(v)
    elseif type(v) == 'number' then
        counts.numbers = counts.numbers + 1
    end
end

local function calc_counts(t, recur, seen)
    if not seen then seen = {} end
    if seen[t] then return end

    seen[t] = true
    counts.tables = counts.tables + 1

    for k, v in pairs(t) do
        update_counts_for(k)
        update_counts_for(v)
        if type(v) == 'table' then
            calc_counts(v, true, seen)
        end
    end
end

local function calc(t)
    for k, _ in pairs(counts) do
        counts[k] = 0
    end

    calc_counts(t, true, {})
end
test_tbl = {1, 2, 3, 4}
calc(_G)
ptab(counts)
